<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>{{project}} milestones</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
	<script src="/js/protovis-r3.2.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript" charset="utf-8">
        $(document).ready(function(){
            $('div.milestone h2').click(function(e){
                if($(e.target).is('h2')){
                    $(this).parent().find('.contents').slideToggle();
                }
            });
        });
	</script>
	<style type="text/css" media="screen">
	    html, body {
	        overflow-x:hidden;
	        padding:0;
	        margin:0;
	        width:100%;
            background-color:#F8F5F1;
            font-size:16px;
	    }

	   div#instructions {
	       width:500px;
	       margin-left:auto;
	       margin-right:auto;
	       padding:10px;
	       margin-top:10px;
	   }

	   div#instructions h1 {
	       font-size:20px;
/*         font-weight:normal;*/
	       text-align:center;
	   }

	   div#instructions h2 {
	       font-size:16px;
/*         font-weight:normal;*/
	       text-align:center;
	   }

	   dt {
	       font-family:monospace;
	       padding:2px;
	       margin:0;
	       margin-left:10px;
	       color:#067100;
	       background-color:#F5EFC7;
	       width:auto;
	       display:inline;
	       border:solid 1px #A8997F;
	   }

	   dd {
	       padding:15px;
	   }
        	   
	   div.milestone {
	       position:relative;
	   }
	   
	   div.milestone h2, div.milestone h2 a.title, div.milestone h2 a.title:visited {
           background-color:#E5ECF9;
           border-top:3px solid #C3D9FF;
           color:#333333;
           font-family:sans-serif;
           padding:0.3em;
       }
       
       div.milestone h2 {
           margin: 0px;
       }
              
	   div.milestone p.title {
	       position:absolute;
	       top:2.5em;
	       left:1em;
	       background-color:#E5ECF9;
	       padding:.2em;
	       -moz-border-radius:5px;
	       -webkit-border-radius:5px;
	       border-radius:5px;
	       display:none;
	   }
	   
	   div.milestone.past h2 {
	       text-decoration:line-through;
	   }
	   
	   div.milestone div.contents {
/*         display:none;*/
	   }
	   
	   div.milestone.current div.contents {
	       display:block;
	   }
	   
	   span.due, span.days_left, a.slug {
	       font-style: italic;
	       font-weight:normal;
	       font-size:.8em;
	       color:#5D666D;
	   }
	   
	   span.days_left {
	       float:right;
	       margin-right:10px;
	   }
	   
	   span.clickTicket {
	       color:#647A89;
	   }
	   
	   div#milestones {
	       width:1100px;
	       padding:0;
	       margin:0;
	       margin-left:auto;
	       margin-right:auto;
	       background-color:white;
/*         -moz-box-shadow: 0px 0px 10px #333;
           -webkit-box-shadow: 0px 0px 10px #333;
           box-shadow: 0px 0px 10px #333;
*/	       z-index:1;
            border:solid 2px #E5ECF9;
	   }
	   
	   div#about {
	       padding:1em;
	       text-align:center;
	   }
	   
	   div#about h1 {
	       font-size:18px;
	       font-weight:normal;
	   }
	   	   
	   a.slug {
	       display:none;
	       margin-left:10px;
	   }
	   
	   h2:hover a.slug {
	       display:inline;
	   }
	   
	   h2 a.all_issues {
	       font-size:14px;
	       margin-left:10px;
	       color:#768597;
	   }
	   
	   p.closed {
	       padding:1em;
	   }
	   
	</style>
</head>
<body>
    <div id="instructions">
        <h1>Instructions</h1>
        <p>
            These charts break down how much work has been assigned to each
            contributor in relation to a milestone. The number of days left to
            work on a milestone is calculated to include weekdays and exclude 
            holidays.
        </p>
        <p>
            Each issue in the
            <a href="http://code.google.com/p/{{project}}/issues/">Issue Tracker</a>
            is represented by a different color in the stacked bar chart. View 
            more information about a issues by hovering over the bars. Click
            on them to see the issue in the issue tracker.
        </p>
        <h2>Configuring</h2>
        <p>
            The calendars that contain 
            <a href="{{settings.MILESTONES_LINK}}">Milestones</a> and 
            <a href="{{settings.HOLIDAYS_LINK}}">Holidays</a> are Google 
            Calendars, and so can be accessed and modified by anyone with 
            appropriate permission.
        </p>
        <p>
            How issues from the <a href="http://code.google.com/p/{{project}}/issues/">Issue Tracker</a>
            are labelled affect their display in these charts. You can change
            these labels and refresh this page to see changes immediately.
        </p>
        <dl>
            <dt>Milestone-*</dt>
            <dd>
                This label assigns an issue to a particular milestone. Any 
                issues not given a milestone label can be found in the 
                <a href="#unassigned">unassigned issues list</a>. 
                To avoid confusion, the administrator of this system should
                enter milestones into the 
                <a href="{{settings.MILESTONES_LINK}}">milestone calendar</a>,
                and then add those milestones to the "Predefined issue labels"
                in the Google Project Hosting admin interface. Note that 
                the names of labels and milestones in the calendar must match exactly.
            </dd>
            
            <dt>EstimatedTime-*</dt>
            <dd>
                This label specifies the number of hours the issue is expected
                to take to resolve. Use only round numbers here. Your admin
                can enter predefined labels into the issue tracker, but any
                value can be appended to <tt>EstimatedTime-</tt> as long as it is a
                round number. <strong>Tickets without a time estimate are 
                assumed to be 1 hour.</strong> This is designed to encourage
                developers to fill in estimates for tickets assigned to them.
            </dd>
            
            <dt>Owner</dt>
            <dd>
                Setting the owner has the expected result of adding the issue
                to the user's total workload. Any issues not given an owner
                can be found under "Unassigned".
            </dd>
        </dl>
    </div>
    <div id="milestones">
	{% for milestone in milestones %}
		<div id="{{milestone.slug}}" class="milestone{% ifequal milestone.workdays_left 0 %} past{% endifequal %}{% if milestone.current %} current{% endif %}">
			<h2>{{milestone.title}} {% ifequal milestone.workdays_left 0 %}{% else %}<span class="due">due {{ milestone.date|date:"D d M Y" }}</span><a class="all_issues" href="http://code.google.com/p/{{project}}/issues/list?q=label:{{milestone.title}}">view in issue tracker</a><span class="days_left">{{milestone.workdays_left}} working days {% if milestone.current %}left{% else %} in milestone{% endif %}</span>{% endifequal %}<a class="slug" href="#{{milestone.slug}}">¶</a></h2>
			<div class="contents">
    			<p class="title"></p>
    			{% ifequal milestone.workdays_left 0 %}
    			    <p class="closed">All issues closed.</p>
    			{% else %}
                <script type="text/javascript+protovis">
                    // Setup the data
                    var j = '{{milestone.tickets_json}}';
                    var data;
                    if(window.JSON){
                        data = JSON.parse(j);
                    }else{
                        data = eval('('+j+')');
                    }
                    var days_left = {{milestone.workdays_left}};
                    var hours_left = days_left * 8;
                    // All this is to get a data structure that is an array with 
                    // an array of tickets for each user
                    var tickets_by_owner = pv.nest(data).key(function(d) d.owner).map();
                    var data = [];
                    var sums = [];
                    var owners = [];
                    for(var key in tickets_by_owner){
                        sums.push(pv.sum(tickets_by_owner[key], function(d) d.hours));
                        data.push(tickets_by_owner[key]);
                        owners.push(key);
                    }
                    // Data has to be transposed to work with the stacked layout
                    pv.transpose(data);

                    // Dimensions
                    var w = 900
                    var barHeight = 40;
                    var h = (owners.length + 1) * (barHeight + 10) // top+bottom margin included;
                    var max_days_for_single_user = pv.max(sums)

                    // Scales
                    var x = pv.Scale.linear(0, max_days_for_single_user * 1.05).range(0, w);
                    var y = pv.Scale.ordinal(owners).splitBanded(0, h, 0);
                    var colors = pv.Scale.linear(0, 1000).range("#1f77b4", "#aec7e8");

                    var vis = new pv.Panel()
                        .width(w)
                        .height(h)
                        .left(200) // margins
                        .top(60)
                        .bottom(80)

                    var bar = vis.add(pv.Panel)
                        .data(data)
                        .add(pv.Bar)
                            .visible(function(d) !!d) // transposing the data results in null's in the array
                            .data(function(d) d)
                            .top(function() y(this.index))
                            .height(barHeight)
                            .left(pv.Layout.stack())
                            .width(function(d) x(d.hours))
                            .fillStyle(function(d) colors(Math.random() * 1000))
                            .event("mouseover", function(d){
                                var el = $('#{{milestone.slug}} p.title');
                                el.html(d.title + ' - '+d.hours+' hours' + ' <span class="clickTicket">click to view in issue tracker</span>');
                                el.show();
                            })
                            .event("mouseout", function(d){
                                $('#{{milestone.slug}} p.title').hide();
                            })
                            .event('click', function(d) window.open('http://code.google.com/p/{{project}}/issues/detail?id='+d.id))

                    vis.add(pv.Rule)
                        .visible(function(d) d != 0) // the transpose operation adds undefined array elements
                        .data([hours_left])
                        .left(function(d) x(d))
                        .strokeStyle("red")
                        .anchor('top').add(pv.Label)
                            .text("Milestone Deadline")
                            .textMargin(10)
                            .textStyle("red")

                    vis.add(pv.Rule)
                        .visible(function(d) d != hours_left)
                        .data(pv.range(0, max_days_for_single_user, 8))
                        .left(function(d) x(d))
                        .height(5)
                        .top(10)
                        .strokeStyle("black")
                        .anchor('top').add(pv.Label)
                            .text(function(d) d/8)

                    bar.anchor('left').add(pv.Label)
                        .visible(function() !this.parent.index)
                        .textAlign('right')
                        .text(function(d) d.owner)
                        .font("14px sans-serif")
                        .textMargin(15)

                    vis.render();

                </script>			 
                {% endifequal %}
			</div>
		</div>
	{% endfor %}
	    <div id="unassigned" class="milestone">
	        <h2>Issues without a Milestone</h2>
	        <div class="contents">
                <script type="text/javascript+protovis">

                var data = eval('({{unassigned}})');
                console.log(data);
                var w = 400;
                var h = 400;
                var r = w / 2;
                var t = 30;
                var a = pv.Scale.linear(0, pv.sum(data, function(d) d.hours)).range(0, 2 * Math.PI);

                var vis = new pv.Panel()
                    .width(w)
                    .height(h)
                    .top(20)
                    .left(20)
                    .bottom(20);

                vis.add(pv.Wedge)
                    .data(data)
                    .bottom(w / 2)
                    .left(w / 2)
                    .innerRadius(r - t)
                    .outerRadius(r)
                    .angle(function(d) a(d.hours))
                    .title(function(d) d.title)
                    .event('click', function(d) window.open('http://code.google.com/p/{{project}}/issues/detail?id='+d.id))
                  .anchor("outer").add(pv.Label)
                    .textMargin(t + 5)
                    .text(function(d) 'issue ' + d.id)
                    .event('click', function(d) window.open('http://code.google.com/p/{{project}}/issues/detail?id='+d.id));
                    
                    
                vis.render();

                </script>
	        </div>
	    </div>
	</div>
	<div id="about">
	   <h1>about google-code-reporting</h1>
	   <p>
	       <a href="http://code.google.com/p/google-code-reporting/">google-code-reporting</a> is an open-source application 
	       that integrates with the Google Project Hosting Issue Tracker and 
	       runs on App Engine.
	   </p>
	</div>
</body>
</html>
